package br.com.thor.persistencia;

import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

import br.com.thor.biblioteca.mensageria.config.ResolucaoTipoException;
import br.com.thor.biblioteca.mensageria.config.ResolutorTipo;
import br.com.thor.biblioteca.mensageria.servico.ServicoBase;
import br.com.thor.biblioteca.persistencia.IEntidadeDbLib;
import br.com.thor.biblioteca.persistencia.mensagens.ConsultarObjetoRequest;
import br.com.thor.biblioteca.persistencia.mensagens.ConsultarObjetoResponse;
import br.com.thor.biblioteca.persistencia.mensagens.ReceberObjetoRequest;
import br.com.thor.biblioteca.persistencia.mensagens.ReceberObjetoResponse;
import br.com.thor.biblioteca.persistencia.mensagens.RemoverObjetoRequest;
import br.com.thor.biblioteca.persistencia.mensagens.RemoverObjetoResponse;
import br.com.thor.biblioteca.persistencia.mensagens.SalvarObjetoRequest;
import br.com.thor.biblioteca.persistencia.mensagens.SalvarObjetoResponse;


public class PersistenciaDB extends ServicoBase implements IPersistenciaDB {

	private static ResolutorTipo resolutorTipo = null;
	private static Map<Class<?>, Method> mapaMetodos = new HashMap<Class<?>, Method>();
	//private Class<T> type;
	
	@SuppressWarnings("unchecked")
	public static <T> ConsultarObjetoResponse<T> consultarObjeto(Class<T> classe, ConsultarObjetoRequest<T> parametros) throws Throwable{
		IEntidadeDbLib entidadeDbLib = getDbLib(classe);
		return getDbLib(classe).consultarObjeto(parametros);
	}
	
	public static <T> ReceberObjetoResponse<T> receberObjeto(Class<T> classe, ReceberObjetoRequest<T> parametros) throws Throwable{
		return (ReceberObjetoResponse<T>) repassarMensagem(classe, parametros, "ReceberObjeto");
	}
	
	public static <T> SalvarObjetoResponse<T> salvarObjeto(Class<T> classe, SalvarObjetoRequest<T> parametros) throws Throwable{
		IEntidadeDbLib entidadeDbLib = getDbLib(classe);
		return getDbLib(classe).salvarObjeto(parametros);
	}
	
	public static <T> RemoverObjetoResponse<T> removerObjeto(Class<T> classe, RemoverObjetoRequest<T> parametros) throws Throwable{
		return (RemoverObjetoResponse<T>) repassarMensagem(classe, parametros, "RemoverObjeto");
	}

	public PersistenciaDB() { 
		try {
			resolutorTipo = ResolutorTipo.getInstance();
			
			// Percorro os métodos
			Method[] metodos = IEntidadeDbLib.class.getMethods();
			for (Method metodo : metodos) {
				// Quando encontrar um método que recebe SOMENTE um parâmetro do tipo
				// br.com.resource.citibank.marc.comum.mensagem.MensagemRequestBase
				// guardo no dicionário
				Class<?>[] parametros = metodo.getParameterTypes();
				mapaMetodos.put(parametros[0], metodo);
				
			}
		} catch (Exception e) {
			// TODO: handle exception
			e.printStackTrace();
		}
		
	}
	
	private static <T> IEntidadeDbLib getDbLib(Class<T> classe){
		IEntidadeDbLib entidadeDbLib = null;
		try {
			Class<? extends IEntidadeDbLib> entidadeDB = 
					(Class<? extends IEntidadeDbLib>) resolutorTipo.getDbLib(classe.getSimpleName().replace("Info", "DbLib"));
			
			entidadeDbLib = entidadeDB.newInstance();
		} catch (InstantiationException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (Exception e) {
			// TODO: handle exception
			e.printStackTrace();
		}
		
		return entidadeDbLib;
	}
	
	private static <T> Object repassarMensagem ( Class<T> T, Object mensagem, String nomeMetodo) throws Throwable{
 
			try {
				Class<? extends IEntidadeDbLib> entidadeDB = 
						(Class<? extends IEntidadeDbLib>) resolutorTipo.getDbLib(T.getSimpleName().replace("Info", "DbLib"));
				Method metodo2 = null;
				// Valida se existe o método
				Class<? extends IEntidadeDbLib> classe = entidadeDB;
				if (!mapaMetodos.containsKey(mensagem.getClass())) {
					throw new ClassNotFoundException("Class '" + classe.getCanonicalName()
							+ "' is not a known class.");
				}
				
				// Obtenho o m�todo atrav�s do tipo da mensagem de requisi��o
				Method metodo = mapaMetodos.get(mensagem.getClass());
				
				// Ativo o servi�o deste m�todo
				Object dbLib = entidadeDB.newInstance();
				
				return metodo.invoke(dbLib, mensagem);
				
			} catch (ResolucaoTipoException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			
			return null;
	}
	
	

}
